package cn.git.agent;

import cn.git.agent.enhance.EnhancedContext;
import cn.git.agent.interceptor.ConstructMethodInterceptPoint;
import cn.git.agent.interceptor.InstanceMethodInterceptPoint;
import cn.git.agent.interceptor.StaticMethodInterceptPoint;
import cn.git.agent.match.ClassMatch;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.dynamic.DynamicType;

/**
 * @description: 所有插件的顶级父类
 * @program: bank-credit-sy
 * @author: lixuchun
 * @create: 2024-12-24
 */
public abstract class AbstractClassEnhancePluginDefine {

    /**
     * 插件增强类中，用于存储增强后的类实例的字段名称
     * 为匹配到的字节码新增的属性名称
     */
    public static final String CONTEXT_ATTR_NAME = "_$enhancedClassField_ws";

    /**
     * 获取需要拦截的类
     *
     * @return
     */
    protected abstract ClassMatch enhanceClass();

    /**
     * 获取需要拦截的方法，实例拦截方法
     *
     * @return
     */
    protected abstract InstanceMethodInterceptPoint[] getInstanceMethodsInterceptPoints();

    /**
     * 获取构造方法的拦截点
     *
     * @return
     */
    protected abstract ConstructMethodInterceptPoint[] getConstructMethodInterceptPoint();

    /**
     * 获取静态方法拦截点
     *
     * @return
     */
    protected abstract StaticMethodInterceptPoint[] getStaticMethodInterceptPoint();

    /**
     * 增强类的主入口
     *
     * @param typeDescription
     * @param builder
     * @param classLoader
     * @param enhancedContext
     * @return
     */
    public DynamicType.Builder<?> define(TypeDescription typeDescription,
                                         DynamicType.Builder<?> builder,
                                         ClassLoader classLoader,
                                         EnhancedContext enhancedContext) {
        // 增强插件类的名字 eg: cn.git.agent.Mysql5Instrumentation
        String pluginsDefineClassName = this.getClass().getName();

        // 要增强的类名称 eg: com.mysql.jdbc.PreparedStatement
        String typeName = typeDescription.getTypeName();
        System.out.println("准备使用" + pluginsDefineClassName + "增强" + typeName + "开始");
        DynamicType.Builder<?> newBuilder = this.enhance(typeDescription, builder, classLoader, enhancedContext);

        // 调用初始化完成，增强类型设置true已增强
        enhancedContext.initializationStageCompleted();

        System.out.println("使用" + pluginsDefineClassName + "增强" + typeName + "成功");
        return newBuilder;
    }

    private DynamicType.Builder<?> enhance(TypeDescription typeDescription,
                                           DynamicType.Builder<?> builder,
                                           ClassLoader classLoader,
                                           EnhancedContext enhancedContext) {
        // 增强静态方法
        builder = this.enhanceStaticClass(typeDescription, builder, classLoader);
        // 增强实例以及构造方法
        builder = this.enhanceInstance(typeDescription, builder, classLoader, enhancedContext);

        return builder;
    }

    /**
     * 增强实例以及构造方法
     * @param typeDescription
     * @param builder
     * @param classLoader
     * @param enhancedContext
     * @return
     */
    protected abstract DynamicType.Builder<?> enhanceInstance(TypeDescription typeDescription,
                                                              DynamicType.Builder<?> builder,
                                                              ClassLoader classLoader,
                                                              EnhancedContext enhancedContext);

    /**
     * 增强静态方法
     * @param typeDescription
     * @param builder
     * @param classLoader
     * @return
     */
    protected abstract DynamicType.Builder<?> enhanceStaticClass(TypeDescription typeDescription,
                                                DynamicType.Builder<?> builder,
                                                ClassLoader classLoader);
}
